-module(vector).

%% Creation functions
-export([new/3, o/0, xn/0, yn/0, zn/0]).
%% Arithmetic functions
-export([xprod/2, dprod/2, sum/2, dif/2, scale/2, sdiv/2]).
%% Rotations
-export([rotx/2, roty/2, rotz/2, rot/2]).
%% Translations
-export([tx/2, ty/2, tz/2]).
%% Misc functions
-export([split/1, normalize/1, len/1, transform/2, distance/2, ang/1, ang/2]).

-record(v, {x, y, z}).

ang(V1=#v{}, V2=#v{}) ->
    V1n = normalize(V1),
    V2n = normalize(V2),
    Dot = dprod(V1n, V2n),
    math:acos(Dot)*math:acos(Dot).

rot({Rotx, Roty, Rotz}, V) ->
    vector:rotx(Rotx, vector:roty(Roty, vector:rotz(Rotz, V))).

ang(#v{x=X, y=Y, z=Z}) ->
    {
        math:atan2(Z,Y),
        math:atan2(Z,X),
        math:atan2(Y,X)
    }.

distance(V1=#v{}, V2=#v{}) -> len(dif(V1, V2)).

% Transform a vector from an arbitrary 3D transformation matrix.
transform(M=[_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, Ox, Oy, Oz], V=#v{}) ->
    R = transform(lists:sublist(M, 9), V),
    vector:sum(R, vector:new(Ox, Oy, Oz));
transform([T1, T2, T3, T4, T5, T6, T7, T8, T9], #v{x=X, y=Y, z=Z}) ->
    NX = T1*X+T2*Y+T3*Z,
    NY = T4*X+T5*Y+T6*Z,
    NZ = T7*X+T8*Y+T9*Z,
    #v{x=NX, y=NY, z=NZ}.

%% Translations
tx(D, #v{x=X, y=Y, z=Z}) -> #v{x=X+D, y=Y, z=Z}.
ty(D, #v{x=X, y=Y, z=Z}) -> #v{x=X, y=Y+D, z=Z}.
tz(D, #v{x=X, y=Y, z=Z}) -> #v{x=X, y=Y, z=Z+D}.

%% Rotate a vector around the OX axes
rotx(ANG, #v{x=X, y=Y, z=Z}) ->
	Sin = math:sin(ANG),
	Cos = math:cos(ANG),
	#v{x=X, y=(Cos*Y - Sin*Z), z=(Sin*Y+Cos*Z)}.

%% Rotate a vector around the OY axes
roty(ANG, #v{x=X, y=Y, z=Z}) ->
	Sin = math:sin(ANG),
	Cos = math:cos(ANG),
	#v{x=(Cos*X+Sin*Z), y=Y, z=(Cos*Z-Sin*X)}.
	
%% Rotate a vector around the OZ axes
rotz(ANG, #v{x=X, y=Y, z=Z}) ->
	Sin = math:sin(ANG),
	Cos = math:cos(ANG),
	#v{x=(Cos*X-Sin*Y), y=(Sin*X+Cos*Y), z=Z}.

%% Split a vector into it's components
split(#v{x=X, y=Y, z=Z}) -> {X, Y, Z}.

%% Build a new vector
new(X, Y, Z) -> #v{x=X, y=Y, z=Z}.

%% Cross product
xprod(#v{x=X1, y=Y1, z=Z1}, #v{x=X2, y=Y2, z=Z2}) ->
    #v{x=Y1*Z2-Y2*Z1, y=X2*Z1-X1*Z2, z=X1*Y2-Y1*X2}.

%% Normalize a vector
normalize(V=#v{x=X, y=Y, z=Z}) ->
    Length = len(V),
    case Length == 0 of
        true -> o();
        false ->
            IL = 1/Length,
            #v{x=X*IL, y=Y*IL, z=Z*IL}
    end.
	
%% Length of a vector
len(#v{x=X, y=Y, z=Z}) ->
	math:sqrt(X*X + Y*Y + Z*Z).

%% Dot product
dprod(#v{x=X1, y=Y1, z=Z1}, #v{x=X2, y=Y2, z=Z2}) ->
	X1*X2+Y1*Y2+Z1*Z2.

%% Null vector
o() -> #v{x=0, y=0, z=0}.

%% Normals
xn() -> #v{x=1, y=0, z=0}.	% X axes versor
yn() -> #v{x=0, y=1, z=0}.	% Y versor
zn() -> #v{x=0, y=0, z=1}.	% Z versor

%% Sum of 2 vectors or of a vector and a scalar.
sum(#v{x=X1, y=Y1, z=Z1}, #v{x=X2, y=Y2, z=Z2}) -> #v{x=X1+X2, y=Y1+Y2, z=Z1+Z2};
sum(S, #v{x=X, y=Y, z=Z}) -> #v{x=S+X, y=S+Y, z=S+Z}.
	
%% Difference of 2 vectors or of a vector and a scalar
dif(#v{x=X1, y=Y1, z=Z1}, #v{x=X2, y=Y2, z=Z2}) -> #v{x=X1-X2, y=Y1-Y2, z=Z1-Z2};
dif(S, #v{x=X, y=Y, z=Z}) -> #v{x=X-S, y=Y-S, z=Z-S}.

%% Scale a vector by a scalar.
scale(0, _) -> #v{x=0, y=0, z=0};
scale(S, #v{x=X, y=Y, z=Z}) -> #v{x=X*S, y=Y*S, z=Z*S}.

%% Div
sdiv(S, V) -> scale(1/S, V).
	